using System;
using System.Linq;
using EventServer.Core;
using System.Linq.Expressions;
using Raven.Client.Document;
using Raven.Client;
using EventServer.Core.Domain;

namespace EventServer.Infrastructure.Repositories
{
    public class RavenRepository : IRepository
    {
        private const string ConnectionStringName = "Server";

        public T Get<T>(int id) where T : Entity
        {
            return GetEntity(s => s.Load<T>(id.ToString()));
        }

        public T Get<T>(Expression<Func<T, bool>> predicate) where T : Entity
        {
            return GetEntity(s => s.Query<T>().Where(predicate).FirstOrDefault());
        }

        public IQueryable<T> Find<T>() where T : Entity
        {
            return GetQueryable(s => s.Query<T>());
        }

        public IQueryable<T> Find<T>(Expression<Func<T, bool>> predicate) where T : Entity
        {
            return GetQueryable(s => s.Query<T>().Where(predicate));
        }

        public T Save<T>(T entity) where T : Entity
        {
            using (var store = new DocumentStore() { ConnectionStringName = ConnectionStringName })
            {
                store.Initialize();
                var session = store.OpenSession();
                session.Store(entity);
                return session.Load<T>(entity.Id.ToString());
            }
        }

        public T Delete<T>(T entity) where T : Entity
        {
            using (var store = new DocumentStore() { ConnectionStringName = ConnectionStringName })
            {
                store.Initialize();
                var session = store.OpenSession();
                session.Delete<T>(entity);
                return entity;
            }
        }

        private IQueryable<T> GetQueryable<T>(Func<IDocumentSession, IQueryable<T>> dbAction) where T : Entity
        {
            using (var store = new DocumentStore() { ConnectionStringName = ConnectionStringName })
            {
                store.Initialize();
                var session = store.OpenSession();
                return dbAction.Invoke(session);
            }
        }

        private T GetEntity<T>(Func<IDocumentSession, T> dbAction) where T : Entity
        {
            using (var store = new DocumentStore() { ConnectionStringName = ConnectionStringName })
            {
                store.Initialize();
                var session = store.OpenSession();
                return dbAction.Invoke(session);
            }
        }
    }
}
